package com.example.video.controller;

import com.example.video.common.ResultModel;
import com.example.video.common.ResultStatus;
import com.google.common.collect.Maps;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.ExcessiveAttemptsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.annotation.RequiresUser;
import org.apache.shiro.subject.Subject;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;

import static org.springframework.web.bind.annotation.RequestMethod.DELETE;
import static org.springframework.web.bind.annotation.RequestMethod.POST;

/**
 * 认证后台接口
 */
@RestController
@RequestMapping("/login")
@Api(value = "登录相关接口", tags = {"LoginController"})
public class LoginController {

    /**
     * 基于注解
     *
     * @RequiresPermissions 授权处理过程
     * 认证通过后接受 Shiro 授权检查，授权验证时，需要判断当前角色是否拥有该权限。
     * 只有授权通过，才可以访问受保护 URL 对应的资源，否则跳转到“未经授权页面”。
     * 如果我们自定义Realm实现，比如我后面的例子中，自定义了ShiroDbRealm类，当访问被@RequiresPermissions注解的方法时，会先执行ShiroDbRealm.doGetAuthorizationInfo()进行授权。
     * eg:@RequiresPermissions (value={"user:a", "user:b"}, logical= Logical.OR)
     * @RequiresAuthentication 可以用户类/属性/方法，用于表明当前用户需是经过认证的用户。   要求当前Subject 已经在当前的session 中被验证通过才能被访问或调用
     * @RequiresGuest 要求当前的Subject 是一个"guest"，也就是说，他们必须是在之前的session 中没有被验证或被记住才能被访问或调用
     * @RequiresRoles("administrator") 要求当前的Subject 拥有所有指定的角色。如果他们没有，则该方法将不会被执行，而且AuthorizationException 异常将会被抛出。
     * @RequiresUser RequiresUser 注解需要当前的Subject 是一个应用程序用户才能被注解的类/实例/方法访问或调用。一个“应用程序用户”被定义为一个拥有已知身份，或在当前session 中由于通过验证被确认，或者在之前session 中的'RememberMe'服务被记住。
     */

    @RequestMapping(method = POST)
    @ApiOperation(value = "登录")
    @ApiImplicitParams({
            @ApiImplicitParam(dataType = "String", name = "username", value = "账号", required = true, paramType = "query"),
            @ApiImplicitParam(dataType = "String", name = "password", value = "密码", required = true, paramType = "query")
    })
    public Object login(@RequestParam("username") String username,
                        @RequestParam("password") String password) {
        Map<String, Object> map = Maps.newHashMap();
        Subject user = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        try {
            user.login(token);
            map.put("token", user.getSession().getId());
        } catch (LockedAccountException lae) {
            token.clear();
            return ResultModel.error(ResultStatus.ADMIN_AUTHC_ERR, "用户已经被锁定不能登录，请与管理员联系！");
        } catch (ExcessiveAttemptsException e) {
            token.clear();
            return ResultModel.error(ResultStatus.ADMIN_AUTHC_ERR, "登录失败次数过多,锁定10分钟!");
        } catch (AuthenticationException e) {
            token.clear();
            return ResultModel.error(ResultStatus.ADMIN_AUTHC_ERR, "用户或密码不正确！");
        }
        return ResultModel.ok(map);
    }

    @RequiresUser
    @RequestMapping(method = DELETE)
    @ApiOperation(value = "退出")
    public Object logout() {
        SecurityUtils.getSubject().logout();
        return ResultModel.ok();
    }
}
